package com.zm.study.common.excel;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Excel操作
 *
 * @param <T>
 */
public abstract class BaseExcelModel<T> implements Serializable {

    private static final Logger logger = LoggerFactory.getLogger(BaseExcelModel.class);

    public final static int diskSize = 100000;
    public final static int pageSize = 10000;

    /**
     * 获取数据
     *
     * @param start
     * @param limit
     * @return
     */
    public abstract List<T> getDataList(int start, int limit);

    /**
     * 获取表头样式<br>
     * 可根据需求在子类重写
     *
     * @param wb
     * @return
     */
    protected CellStyle getTitleStyle(Workbook wb) {
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        Font font = wb.createFont();
        font.setFontHeightInPoints((short) 18);
        cellStyle.setFont(font);
        return cellStyle;
    }

    /**
     * 获取列头样式<br>
     * 可根据需求在子类重写
     *
     * @param wb
     * @return
     */
    protected CellStyle getHeaderStyle(Workbook wb) {
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        Font font = wb.createFont();
        font.setBold(true);
        cellStyle.setFont(font);
        return cellStyle;
    }

    /**
     * 获取数据样式<br>
     * 可根据需求在子类重写
     *
     * @param wb
     * @return
     */
    protected CellStyle getDataStyle(Workbook wb) {
        return null;
    }


    private List<ExcelColumn> cols;

    public BaseExcelModel(List<ExcelColumn> cols, Class<T> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        Map<String, Field> fieldMap = new HashMap<>();
        for (Field field : fields) {
            fieldMap.put(field.getName(), field);
        }
        for (ExcelColumn col : cols) {
            Field field = fieldMap.get(col.getKey());
            // 设置实体类私有属性可访问
            field.setAccessible(true);
            col.setField(field);
        }
        this.cols = cols;
    }

    public BaseExcelModel(List<ExcelColumn> cols) {
        this.cols = cols;
    }

    public void export(String title, OutputStream outputStream) {
        export(title, -1, outputStream);
    }

    public void export(String title, int excelCount, OutputStream outputStream) {
        Workbook workbook = new SXSSFWorkbook(diskSize);
        Sheet sheet = workbook.createSheet();
        Row row;
        Cell cell;
        int rowOffset = 0;
        // 表头
        row = sheet.createRow(rowOffset);
        cell = row.createCell(0);
        cell.setCellValue(title);
        cell.setCellStyle(getTitleStyle(workbook));
        rowOffset++;
        //合并单元格（ 起始行, 终止行, 起始列, 终止列）
        CellRangeAddress cra = new CellRangeAddress(0, 0, 0, cols.size() - 1);
        sheet.addMergedRegion(cra);

        // 列头
        row = sheet.createRow(rowOffset);
        rowOffset++;
        CellStyle cellStyle = getHeaderStyle(workbook);
        for (int cellNum = 0; cellNum < cols.size(); cellNum++) {
            ExcelColumn col = cols.get(cellNum);
            cell = row.createCell(cellNum);
            cell.setCellValue(col.getText());
            cell.setCellStyle(cellStyle);
        }

        CellStyle dataStyle = getDataStyle(workbook);

        int start = 0;
        int limit = (-1 == excelCount ? pageSize : Math.min(pageSize, excelCount));
        boolean nextPage = true;
        while (nextPage) {
            List<T> dataList = getDataList(start, limit);
            if (null == dataList || dataList.size() == 0) {
                break;
            }
            nextPage = pageSize == dataList.size();
            int endNo = nextPage ? limit : dataList.size() + start;
            for (int j = start; j < endNo; j++) {
                row = sheet.createRow(j + rowOffset);
                // 得到导出对象.
                T vo = dataList.get(j - start);
                if (vo instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) vo;
                    for (int k = 0; k < cols.size(); k++) {
                        ExcelColumn col = cols.get(k);
                        cell = row.createCell(k);
                        if (null != dataStyle) {
                            cell.setCellStyle(dataStyle);
                        }
                        Object o = map.get(col.getKey());
                        cell.setCellValue(o == null ? "" : String.valueOf(o));

                    }
                } else {
                    for (int k = 0; k < cols.size(); k++) {
                        ExcelColumn col = cols.get(k);
                        // 获得field
                        Field field = col.getField();
                        cell = row.createCell(k);
                        if (null != dataStyle) {
                            cell.setCellStyle(dataStyle);
                        }
                        String value = null;
                        try {
                            cell.setCellValue(field.get(vo) == null ? "" : value == null ? String.valueOf(field.get(vo)) : value);
                        } catch (IllegalAccessException e) {
                            logger.error("", e);
                        }

                    }
                }


            }
            start += pageSize;
            limit += (-1 == excelCount ? pageSize : Math.min(pageSize, excelCount - start));
        }

        try {
            outputStream.flush();
            workbook.write(outputStream);
            outputStream.close();
        } catch (IOException e) {
            logger.error("", e);
        }
    }

}
